

#include "lua_string.h"

/********************************************************************************/

static int luac_isalnum(lua_State* L) {
  const char* p = luaL_checkstring(L, 1);
  while (*p) {
    if (isalnum(*p++) == 0) {
      lua_pushboolean(L, 0);
      return 1;
    }
  }
  lua_pushboolean(L, 1);
  return 1;
}

static int luac_isalpha(lua_State* L) {
  const char* p = luaL_checkstring(L, 1);
  while (*p) {
    if (isalpha(*p++) == 0) {
      lua_pushboolean(L, 0);
      return 1;
    }
  }
  lua_pushboolean(L, 1);
  return 1;
}

static int luac_trim(lua_State* L) {
  size_t size;
  const char* p = luaL_checklstring(L, 1, &size);
  if (size == 0) {
    return 1;
  }
  const char* p1 = p, *p2 = p + size - 1;
  while (*p1) {
    if (*p1 == ' ') { p1++; continue; }
    if (*p2 == ' ') { p2--; continue; }
    break;
  }
  lua_pushlstring(L, p1, p2 - p1 + 1);
  return 1;
}

static int luac_icmp(lua_State* L) {
  const char* a = luaL_checkstring(L, 1);
  const char* b = luaL_checkstring(L, 2);
  lua_pushboolean(L, stricmp(a, b) == 0 ? 1 : 0);
  return 1;
}

static int luac_split(lua_State* L) {
  const char *s = luaL_checkstring(L, 1);
  const char *sep = luaL_checkstring(L, 2);
  const char *e;
  int i = 1;

  lua_newtable(L);  /* result */
                    /* repeat for each separator */
  while ((e = strchr(s, *sep)) != NULL) {
    lua_pushlstring(L, s, e-s);  /* push substring */
    lua_rawseti(L, -2, i++);
    s = e + 1;  /* skip separator */
  }
  /* push last substring */
  lua_pushstring(L, s);
  lua_rawseti(L, -2, i);
  return 1;  /* return the table */
}

static void bin2hex(const char *data, size_t size, std::string& out) {
  unsigned char *p = (unsigned char*)data;
  // Show 16 bytes each line.
  size_t lines = size / 16;
  size_t char_last_line = 0;
  if(0 != size % 16){
    char_last_line = size - lines * 16;
  }
  char buf[16];
  unsigned int i = 0;
  for (i = 0; i < lines; ++i){
    out += (sprintf(buf, "%08X ", i * 16), buf);
    int j = 0;
    for (j = 0; j < 16; ++j){
      out += (sprintf(buf, "%02X ", p[16 * i + j]), buf);
      if(j == 7)
        out += "- ";
    }
    out += "    ";
    for (j = 0; j < 16; ++j){
      if (isprint(p[16 * i + j]) && p[16 * i + j] != '%')
        out += (char)p[16 * i + j];
      else
        out += ".";
    }
    out += "\n";
  }
  if (0 != char_last_line){
    out += (sprintf(buf, "%08X ", i * 16), buf);
    size_t j = 0;
    for (j = 0; j < char_last_line; ++j)
    {
      out += (sprintf(buf, "%02X ", p[16 * i + j]), buf);
      if (j == 7 && char_last_line > 8)
        out += "- ";
    }
    size_t k = 0;
    k += ((16 - char_last_line) * 3);
    if (char_last_line <= 8)
      k += 2;
    for (size_t kk = 0; kk < k; ++kk)
      out += " ";
    out += "    ";
    for (j = 0; j < char_last_line; ++j)
    {
      if (isprint(p[16 * i + j]) && p[16 * i + j] != '%')
        out += (char)p[16 * i + j];
      else
        out += ".";
    }
    out += "\n";
  }
}

static int luac_hex(lua_State* L) {
  size_t n = 0;
  const char* p = luaL_checklstring(L, 1, &n);
  std::string out("\n");
  bin2hex(p, n, out);
  lua_pushlstring(L, out.c_str(), out.size() - 1);
  return 1;
}

/********************************************************************************/

LUAOS_API int luaopen_lstring(lua_State* L) {
  const luaL_Reg methods[] = {
    { "split",    luac_split    }, /* string.split(s, r) */
    { "trim",     luac_trim     }, /* string.trim(s)     */
    { "icmp",     luac_icmp     }, /* string.icmp(a, b)  */
    { "hex",      luac_hex      }, /* string.hex(s)      */
    { "isalnum",  luac_isalnum  }, /* string.isalnum(s)  */
    { "isalpha",  luac_isalpha  }, /* string.isalpha(s)  */
    { NULL,       NULL          }
  };
  return new_module(L, "string", methods);
}

/********************************************************************************/
